package com.simpou.commons.utils.pagination;

import java.io.Serializable;

import java.util.ArrayList;
import java.util.List;


/**
 * Auxilia na paginação de grandes quantidades de dados.
 *
 * @author Jonas Pereira
 * @since 2011-06-20
 * @version 2012-07-16
 */
public class Paginator implements Serializable {
    /**
     * Configuração da paginação.
     */
    private final PaginationConfig config;

    /**
     * Página corrente.
     */
    private int actualPage;

    /**
     * Última página.
     */
    private final int lastPage;

    /**
     * Configura e inicializa a paginação.
     *
     * @param pagConfig Configuração.
     */
    public Paginator(final PaginationConfig pagConfig) {
        this.config = pagConfig.clone();
        this.actualPage = 1;
        this.lastPage = Math.max((int) Math.ceil(
                    (float) (pagConfig.getElements()) / (float) pagConfig.getPageSize()),
                1);
    }

    /**
     * Próxima página.
     *
     * @return a {@link PageLimits} object.
     */
    public PageLimits goNextPage() {
        if (actualPage < lastPage) {
            actualPage++;
        }

        return getLimits();
    }

    /**
     * Página anterior.
     *
     * @return a {@link PageLimits} object.
     */
    public PageLimits goPreviousPage() {
        if (actualPage > 1) {
            actualPage--;
        }

        return getLimits();
    }

    /**
     * Primeira página.
     *
     * @return a {@link PageLimits} object.
     */
    public PageLimits goFirstPage() {
        actualPage = 1;

        return getLimits();
    }

    /**
     * Última página.
     *
     * @return Limites.
     */
    public PageLimits goLastPage() {
        actualPage = lastPage;

        return getLimits();
    }

    /**
     * Vai para uma página.
     *
     * @param page Página.
     * @return Limites.
     */
    public PageLimits goToPage(final int page) {
        if ((page < 1) || (page > lastPage)) {
            throw new IllegalArgumentException("Page " + page);
        } else {
            this.actualPage = page;
        }

        return getLimits();
    }

    /**
     * <p>getLimits.</p>
     *
     * @return Limites da página atual.
     */
    public PageLimits getLimits() {
        int offset = getOffset();
        int lastElement = (offset + config.getPageSize()) - 1;
        int size;

        if (lastElement >= config.getElements()) {
            size = (int) (config.getElements() - offset);
        } else {
            size = config.getPageSize();
        }

        PageLimits limits = new PageLimits(offset, size);

        return limits;
    }

    /**
     * <p>getPages.</p>
     *
     * @return Lista de páginas.
     */
    public List<Integer> getPages() {
        int halfMaxPagesToShow = config.getPagesToShow() / 2;
        int first;
        int last;

        if (countPages() <= config.getPagesToShow()) {
            first = 1;
            last = countPages();
        } else {
            if ((config.getPagesToShow() % 2) > 0) {
                first = Math.max(1, getActualPage() - halfMaxPagesToShow);
            } else {
                first = Math.max(1, getActualPage() - halfMaxPagesToShow + 1);
            }

            last = Math.min((first + config.getPagesToShow()) - 1, countPages());

            if (last == countPages()) {
                first = last - config.getPagesToShow() + 1;
            }
        }

        List<Integer> pages = new ArrayList<Integer>();

        for (int i = first; i <= last; i++) {
            pages.add(i);
        }

        return pages;
    }

    /**
     * <p>Getter for the field <code>actualPage</code>.</p>
     *
     * @return Página corrente.
     */
    public int getActualPage() {
        return actualPage;
    }

    /**
     * <p>Getter for the field <code>lastPage</code>.</p>
     *
     * @return Última página.
     */
    public int getLastPage() {
        return lastPage;
    }

    /**
     * <p>countPages.</p>
     *
     * @return Númeor de páginas.
     */
    public int countPages() {
        return lastPage;
    }

    /**
     * <p>Getter for the field <code>config</code>.</p>
     *
     * @return Cópia da configuração.
     */
    public PaginationConfig getConfig() {
        return config.clone();
    }

    /**
     * @return Número de dados a partir do primeiro da página.
     */
    private int getOffset() {
        return (actualPage - 1) * config.getPageSize();
    }
}
